package com.wlps.filter;

import com.wlps.config.CustomerUserDetailsService;
import com.wlps.contant.Contant;
import com.wlps.controller.CertificationController;
import com.wlps.entity.SysUser;
import com.wlps.exception.ImageCodeException;
import com.wlps.exception.TokenException;
import com.wlps.handler.LoginFailureHandler;
import com.wlps.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *
 * </p>
 *
 * @author ZWYZY
 * @since 2020/7/28
 */
@Slf4j
@Component("checkTokenFilter")
public class CheckTokenFilter extends OncePerRequestFilter {
    @Value("${wlps.loginUrl}")
    private String loginUrl;
    @Value("${wlps.imgUrl}")
    private String imgUrl;
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private CustomerUserDetailsService customerUserDetailsService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
            response, FilterChain filterChain) throws ServletException, IOException {
        String url = request.getRequestURI();
        if (url.equals(loginUrl)) {
            try {
//                validate(request);
            } catch (AuthenticationException e) {
                loginFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
        }
        else {
//验证token,验证码请求不需要验证token
            String imgurl = request.getRequestURI();
            if (!imgurl.equals(imgUrl)) {
                try {
                    validateToken(request, response);
                } catch (AuthenticationException e) {
                    loginFailureHandler.onAuthenticationFailure(request, response, e);
                    return;
                }
            }
        }
        filterChain.doFilter(request, response);
    }


    //验证token
    private void validateToken(HttpServletRequest request, HttpServletResponse response) {
//获取前端传来的token
        String token = request.getHeader("token");
        System.out.println(token);
//解析token，获取用户名
        String username = jwtUtils.getUsernameFromToken(token);
        if (jwtUtils.isExpire(token)) {
            SysUser sysUser =
                    (SysUser) customerUserDetailsService.loadUserByUsername(username);

            token = jwtUtils.generateToken(sysUser);

//            redisTemplate.opsForValue().set(Contant.USERDETAILS_KEY+sysUser.getUsername(),sysUser,50,TimeUnit.MINUTES);
            redisTemplate.opsForValue().set(Contant.TOKEN_KEY + token, sysUser.getId(),50,TimeUnit.MINUTES);
            redisTemplate.opsForValue().set(Contant.TOKEN_ID_KEY + sysUser.getId().toString(), token,50,TimeUnit.MINUTES);
            response.setHeader("token", token);

        }
//如果token或者用户名为空的话，不能通过认证
        if (StringUtils.isBlank(token) || StringUtils.isBlank(username)) {
            if (redisTemplate.hasKey(Contant.TOKEN_KEY + token)) {
                Integer userId = (Integer) redisTemplate.opsForValue().get(Contant.TOKEN_KEY + token);
                redisTemplate.delete(Contant.TOKEN_KEY + token);
                if (redisTemplate.hasKey(Contant.TOKEN_ID_KEY + userId)) {
                    redisTemplate.delete(Contant.TOKEN_ID_KEY + userId);
                    redisTemplate.opsForValue().decrement(Contant.ONLINEUSER_KEY);
                }
            }
            throw new TokenException("token验证失败!");
        }
        if (!redisTemplate.hasKey(Contant.TOKEN_KEY + token)) {
            throw new TokenException("用户未登录");
        }
        Integer userId = (Integer) redisTemplate.opsForValue().get(Contant.TOKEN_KEY + token);
        if (redisTemplate.hasKey(Contant.TOKEN_ID_KEY + userId) && !token.equals(redisTemplate.opsForValue().get(Contant.TOKEN_ID_KEY + userId))) {
            throw new TokenException("您的账号已在异地登录,请重新登录");
        }
        UserDetails userDetails =
                customerUserDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new TokenException("token验证失败!");
        }

        UsernamePasswordAuthenticationToken authentication = new
                UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        authentication.setDetails(new
                WebAuthenticationDetailsSource().buildDetails(request));
//设置为已登录
        SecurityContextHolder.getContext().setAuthentication(authentication);


    }

    //验证验证码
    private void validate(HttpServletRequest request) {
//1.获取登录请求的验证码
        String inputCode = request.getParameter("code");
//2.获取Session中的验证码

        String code =
                (String) request.getSession().getAttribute(CertificationController.SESSION_KEY);
//3.判断验证码是否为空
        if (StringUtils.isBlank(inputCode)) {
            throw new ImageCodeException("验证码不能为空!");
        }
//4.判断验证码是否相等
        if (!inputCode.equalsIgnoreCase(code)) {
            throw new ImageCodeException("验证码输入错误!");
        }
    }
}
